/**
 * @class Ext.tree.TreeFilter Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
 * @param {TreePanel} tree
 * @param {Object} config (optional)
 */
Ext.tree.TreeFilter = function( tree, config )
{
	this.tree = tree;
	this.filtered = {};
	Ext.apply(this, config);
};

Ext.tree.TreeFilter.prototype =
{
	clearBlank: false,
	reverse: false,
	autoClear: false,
	remove: false,

	/**
	 * Filter the data by a specific attribute.
	 * 
	 * @param {String/RegExp} value Either string that the attribute value should start with or a RegExp to test against the attribute
	 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
	 * @param {TreeNode} startNode (optional) The node to start the filter at.
	 */
	filter: function( value, attr, startNode )
	{
		attr = attr || "text";
		var f;
		if( typeof value == "string" )
		{
			var vlen = value.length;
			// auto clear empty filter
			if( vlen == 0 && this.clearBlank )
			{
				this.clear();
				return;
			}
			value = value.toLowerCase();
			f = function( n )
			{
				return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
			};
		}
		else if( value.exec )
		{ // regex?
			f = function( n )
			{
				return value.test(n.attributes[attr]);
			};
		}
		else
		{
			throw 'Illegal filter type, must be string or regex';
		}
		this.filterBy(f, null, startNode);
	},

	/**
	 * Filter by a function. The passed function will be called with each node in the tree (or from the startNode). If the function returns true, the node is kept
	 * otherwise it is filtered. If a node is filtered, its children are also filtered.
	 * 
	 * @param {Function} fn The filter function
	 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
	 */
	filterBy: function( fn, scope, startNode )
	{
		startNode = startNode || this.tree.root;
		if( this.autoClear )
		{
			this.clear();
		}
		var af = this.filtered, rv = this.reverse;
		var f = function( n )
		{
			if( n == startNode )
			{
				return true;
			}
			if( af[n.id] )
			{
				return false;
			}
			var m = fn.call(scope || n, n);
			if( !m || rv )
			{
				af[n.id] = n;
				n.ui.hide();
				return false;
			}
			return true;
		};
		startNode.cascade(f);
		if( this.remove )
		{
			for( var id in af )
			{
				if( typeof id != "function" )
				{
					var n = af[id];
					if( n && n.parentNode )
					{
						n.parentNode.removeChild(n);
					}
				}
			}
		}
	},

	/**
	 * Clears the current filter. Note: with the "remove" option set a filter cannot be cleared.
	 */
	clear: function()
	{
		var t = this.tree;
		var af = this.filtered;
		for( var id in af )
		{
			if( typeof id != "function" )
			{
				var n = af[id];
				if( n )
				{
					n.ui.show();
				}
			}
		}
		this.filtered = {};
	}
};
